<!-- 树形层级选择器-->
<!-- 1、支持单选、多选 -->
<template>
  <view>
    <view
      class="tree-cover"
      :class="{ show: showDialog }"
      @tap="_cancel"
    ></view>
    <view class="tree-dialog" :class="{ show: showDialog }">
      <view class="tree-bar">
        <view
          class="tree-bar-cancel"
          :style="{ color: cancelColor }"
          hover-class="hover-c"
          @tap="_cancel"
          >取消
        </view>
        <view class="tree-bar-title" :style="{ color: titleColor }">{{
          title
        }}</view>
        <view
          class="tree-bar-confirm"
          :style="{ color: confirmColor }"
          hover-class="hover-c"
          @tap="_confirm"
        >
          {{ multiple ? "确定" : "" }}
        </view>
      </view>
      <view class="tree-view">
        <scroll-view class="tree-list" :scroll-y="true">
          <block v-for="(item, index) in treeList" :key="index">
            <view
              class="tree-item"
              :style="[
                {
                  paddingLeft: item.level * 30 + 'rpx'
                }
              ]"
              :class="{
                itemBorder: border === true,
                show: item.isShow
              }"
            >
              <view class="item-label">
                <view
                  style="margin-top: 3px"
                  class="item-icon uni-inline-item"
                  @tap.stop="_onItemSwitch(item, index)"
                >
                  <view
                    v-if="!item.isLastLevel && item.isShowChild"
                    class="switch-on"
                    :style="{ 'border-left-color': switchColor }"
                  >
                  </view>
                  <view
                    v-else-if="!item.isLastLevel && !item.isShowChild"
                    class="switch-off"
                    :style="{ 'border-top-color': switchColor }"
                  >
                  </view>
                  <view
                    v-else
                    class="item-last-dot"
                    :style="{ 'border-top-color': switchColor }"
                  >
                  </view>
                </view>
                <view
                  style="flex: 1"
                  class="uni-flex-item flex flex-a flex-sb uni-inline-item"
                  @tap.stop="_onItemSelect(item, index)"
                >
                  <view class="item-name">
                    {{
                      item.name +
                      (item.childCount ? "(" + item.childCount + ")" : "")
                    }}
                  </view>
                  <view
                    class="item-check"
                    v-if="selectParent ? true : item.isLastLevel"
                  >
                    <view
                      class="item-check-yes"
                      v-if="item.checkStatus == 1"
                      :class="{ radio: !multiple }"
                      :style="{ 'border-color': confirmColor }"
                    >
                      <view
                        class="item-check-yes-part"
                        :style="{ 'background-color': confirmColor }"
                      >
                      </view>
                    </view>
                    <view
                      class="item-check-yes"
                      v-else-if="item.checkStatus == 2"
                      :class="{ radio: !multiple }"
                      :style="{ 'border-color': confirmColor }"
                    >
                      <view
                        class="item-check-yes-all"
                        :style="{ 'background-color': confirmColor }"
                      >
                      </view>
                    </view>
                    <view
                      class="item-check-no"
                      v-else
                      :class="{ radio: !multiple }"
                      :style="{ 'border-color': confirmColor }"
                    ></view>
                  </view>
                </view>
              </view>
            </view>
          </block>
        </scroll-view>
      </view>
    </view>
  </view>
</template>

<script>
export default {
  emits: ["select-change"],
  name: "ba-tree-picker",
  props: {
    valueKey: {
      type: String,
      default: "id"
    },
    textKey: {
      type: String,
      default: "name"
    },
    childrenKey: {
      type: String,
      default: "children"
    },
    localdata: {
      type: Array,
      default: function () {
        return []
      }
    },
    localTreeList: {
      //在已经格式化好的数据
      type: Array,
      default: function () {
        return []
      }
    },
    selectedData: {
      type: Array,
      default: function () {
        return []
      }
    },
    title: {
      type: String,
      default: ""
    },
    multiple: {
      // 是否可以多选
      type: Boolean,
      default: true
    },
    selectParent: {
      //是否可以选父级
      type: Boolean,
      default: true
    },
    confirmColor: {
      // 确定按钮颜色
      type: String,
      default: "" // #0055ff
    },
    cancelColor: {
      // 取消按钮颜色
      type: String,
      default: "" // #757575
    },
    titleColor: {
      // 标题颜色
      type: String,
      default: "" //
    },
    switchColor: {
      // 节点切换图标颜色
      type: String,
      default: "" // #666
    },
    border: {
      // 是否有分割线 
      type: Boolean,
      default: false
    }
  },
  data() {
    return {
      showDialog: false,
      treeList: []
    }
  },
  computed: {},
  methods: {
    _show() {
      this.showDialog = true
    },
    _hide() {
      this.showDialog = false
    },
    _cancel() {
      this._hide()
      this.$emit("cancel", "")
    },
    _confirm() {
      //多选
      let selectedList = [] //如果子集全部选中，只返回父级 id
      let selectedNames = []
      let currentLevel = -1
      console.log(this.treeList)
      this.treeList.forEach((item, index) => {
        // if (currentLevel >= 0 && item.level > currentLevel) {
        // } else {
        //   if (item.checkStatus === 2) {
        //     currentLevel = item.level
        //     selectedList.push(item.id)
        //     selectedNames = selectedNames
        //       ? selectedNames + " / " + item.name
        //       : item.name
        //   } else {
        //     currentLevel = -1
        //   }
        // }

        if (item.isLastLevel && item.checkStatus) {
          const data = this.getParentId(this.treeList, item.id).map((v) => v.id)
          const names = this.getParentId(this.treeList, item.id)
          selectedNames.push([...names, item].sort((a, b) => a.id - b.id))

          selectedList.push([...data, item.id].sort((a, b) => a - b))
          console.log(selectedList, selectedNames)
        }
      })
      //console.log('_confirm', selectedList);
      this._hide()
      this.$emit("select-change", selectedList, selectedNames)
    },
    getParentId(tree, targetId) {
      const parents = []

      // 封装一个递归函数
      function findParentRecursively(currentId) {
        // 找到当前节点
        const node = tree.find((n) => n.id === currentId)

        // 如果节点存在且有父级
        if (node && node.parentId !== null) {
          // 找到父级节点
          const parentNode = tree.find((n) => n.id === node.parentId)

          // 将父节点存入结果数组
          if (parentNode) {
            parents.push(parentNode)
            // 递归查找父节点的父节点
            findParentRecursively(parentNode.id)
          }
        }
      }

      // 开始递归查找
      findParentRecursively(targetId)

      return parents
    },
    //格式化原数据（原数据为tree结构）
    _formatTreeData(list = [], level = 0, parentItem, isShowChild = true) {
      let nextIndex = 0
      let parentId = -1
      let initCheckStatus = 0
      if (parentItem) {
        nextIndex =
          this.treeList.findIndex((item) => item.id === parentItem.id) + 1
        parentId = parentItem.id
        if (!this.multiple) {
          //单选
          initCheckStatus = 0
        } else initCheckStatus = parentItem.checkStatus == 2 ? 2 : 0
      }
      list.forEach((item) => {
        let isLastLevel = true
        if (item && item[this.childrenKey]) {
          let children = item[this.childrenKey]
          if (Array.isArray(children) && children.length > 0) {
            isLastLevel = false
          }
        }

        let itemT = {
          id: item[this.valueKey],
          name: item[this.textKey],
          level,
          isLastLevel,
          isShow: isShowChild,
          isShowChild: false,
          checkStatus: initCheckStatus,
          orCheckStatus: 0,
          parentId,
          children: item[this.childrenKey],
          childCount: item[this.childrenKey]
            ? item[this.childrenKey].length
            : 0,
          childCheckCount: 0,
          childCheckPCount: 0
        }

        if (this.selectedData.indexOf(itemT.id) >= 0) {
          itemT.checkStatus = 2
          itemT.orCheckStatus = 2
          itemT.childCheckCount = itemT.children ? itemT.children.length : 0
          this._onItemParentSelect(itemT, nextIndex)
        }

        this.treeList.splice(nextIndex, 0, itemT)
        nextIndex++
      })
      //console.log(this.treeList);
    },
    // 节点打开、关闭切换
    _onItemSwitch(item, index) {
      // console.log(item)
      //console.log('_itemSwitch')
      if (item.isLastLevel === true) {
        return
      }
      item.isShowChild = !item.isShowChild
      if (item.children) {
        this._formatTreeData(item.children, item.level + 1, item)
        item.children = undefined
      } else {
        this._onItemChildSwitch(item, index)
      }
    },
    _onItemChildSwitch(item, index) {
      //console.log('_onItemChildSwitch')
      const firstChildIndex = index + 1
      if (firstChildIndex > 0)
        for (var i = firstChildIndex; i < this.treeList.length; i++) {
          let itemChild = this.treeList[i]
          if (itemChild.level > item.level) {
            if (item.isShowChild) {
              if (itemChild.parentId === item.id) {
                itemChild.isShow = item.isShowChild
                if (!itemChild.isShow) {
                  itemChild.isShowChild = false
                }
              }
            } else {
              itemChild.isShow = item.isShowChild
              itemChild.isShowChild = false
            }
          } else {
            return
          }
        }
    },
    // 节点选中、取消选中
    _onItemSelect(item, index) {
      //console.log('_onItemSelect')
      //console.log(item)
      if (!this.multiple) {
        //单选
        item.checkStatus = item.checkStatus == 0 ? 2 : 0

        this.treeList.forEach((v, i) => {
          if (i != index) {
            this.treeList[i].checkStatus = 0
          } else {
            this.treeList[i].checkStatus = 2
          }
        })

        let selectedList = []
        let selectedNames
        selectedList.push(item.id)
        selectedNames = item.name
        this._hide()
        this.$emit("select-change", selectedList, selectedNames)
        return
      }

      let oldCheckStatus = item.checkStatus
      switch (oldCheckStatus) {
        case 0:
          item.checkStatus = 2
          item.childCheckCount = item.childCount
          item.childCheckPCount = 0
          break
        case 1:
        case 2:
          item.checkStatus = 0
          item.childCheckCount = 0
          item.childCheckPCount = 0
          break
        default:
          break
      }
      //子节点 全部选中
      this._onItemChildSelect(item, index)
      //父节点 选中状态变化
      this._onItemParentSelect(item, index, oldCheckStatus)
    },
    _onItemChildSelect(item, index) {
      //console.log('_onItemChildSelect')
      let allChildCount = 0
      if (item.childCount && item.childCount > 0) {
        index++
        while (
          index < this.treeList.length &&
          this.treeList[index].level > item.level
        ) {
          let itemChild = this.treeList[index]
          itemChild.checkStatus = item.checkStatus
          if (itemChild.checkStatus == 2) {
            itemChild.childCheckCount = itemChild.childCount
            itemChild.childCheckPCount = 0
          } else if (itemChild.checkStatus == 0) {
            itemChild.childCheckCount = 0
            itemChild.childCheckPCount = 0
          }
          // console.log('>>>>index：', index, 'item：', itemChild.name, '  status：', itemChild
          // 	.checkStatus)
          index++
        }
      }
    },
    _onItemParentSelect(item, index, oldCheckStatus) {
      //console.log('_onItemParentSelect')
      //console.log(item)
      const parentIndex = this.treeList.findIndex(
        (itemP) => itemP.id == item.parentId
      )
      //console.log('parentIndex：' + parentIndex)
      if (parentIndex >= 0) {
        let itemParent = this.treeList[parentIndex]
        let count = itemParent.childCheckCount
        let oldCheckStatusParent = itemParent.checkStatus

        if (oldCheckStatus == 1) {
          itemParent.childCheckPCount -= 1
        } else if (oldCheckStatus == 2) {
          itemParent.childCheckCount -= 1
        }
        if (item.checkStatus == 1) {
          itemParent.childCheckPCount += 1
        } else if (item.checkStatus == 2) {
          itemParent.childCheckCount += 1
        }

        if (
          itemParent.childCheckCount <= 0 &&
          itemParent.childCheckPCount <= 0
        ) {
          itemParent.childCheckCount = 0
          itemParent.childCheckPCount = 0
          itemParent.checkStatus = 0
        } else if (itemParent.childCheckCount >= itemParent.childCount) {
          itemParent.childCheckCount = itemParent.childCount
          itemParent.childCheckPCount = 0
          itemParent.checkStatus = 2
        } else {
          itemParent.checkStatus = 1
        }
        //console.log('itemParent：', itemParent)
        this._onItemParentSelect(itemParent, parentIndex, oldCheckStatusParent)
      }
    },
    // 重置数据
    _reTreeList() {
      this.treeList.forEach((v, i) => {
        this.treeList[i].checkStatus = v.orCheckStatus
      })
    },
    _initTree() {
      this.treeList = []
      this._formatTreeData(this.localdata)
    }
  },
  watch: {
    localdata() {
      this._initTree()
    },
    localTreeList() {
      this.treeList = this.localTreeList
    },
    selectedData: {
      deep: true,
      handler(val) {
        this._initTree()
      }
    }
  },
  mounted() {
    this._initTree()
  }
}
</script>

<style scoped>
.tree-cover {
  position: fixed;
  top: 0rpx;
  right: 0rpx;
  bottom: 0rpx;
  left: 0rpx;
  z-index: 100;
  background-color: rgba(0, 0, 0, 0.4);
  opacity: 0;
  transition: all 0.3s ease;
  visibility: hidden;
}

.tree-cover.show {
  visibility: visible;
  opacity: 1;
}

.tree-dialog {
  position: fixed;
  top: 0rpx;
  right: 0rpx;
  bottom: 0rpx;
  left: 0rpx;
  background-color: #fff;
  border-top-left-radius: 10px;
  border-top-right-radius: 10px;
  /* #ifndef APP-NVUE */
  display: flex;
  /* #endif */
  flex-direction: column;
  z-index: 102;
  top: 20%;
  transition: all 0.3s ease;
  transform: translateY(100%);
}

.tree-dialog.show {
  transform: translateY(0);
}

.tree-bar {
  /* background-color: #fff; */
  height: 90rpx;
  padding-left: 25rpx;
  padding-right: 25rpx;
  display: flex;
  justify-content: space-between;
  align-items: center;
  box-sizing: border-box;
  border-bottom-width: 1rpx !important;
  border-bottom-style: solid;
  border-bottom-color: #f5f5f5;
  font-size: 32rpx;
  color: #757575;
  line-height: 1;
}

.tree-bar-confirm {
  color: #0055ff;
  padding: 15rpx;
}

.tree-bar-title {
}

.tree-bar-cancel {
  color: #757575;
  padding: 15rpx;
}

.tree-view {
  flex: 1;
  padding: 20rpx;
  /* #ifndef APP-NVUE */
  display: flex;
  /* #endif */
  flex-direction: column;
  overflow: hidden;
  height: 100%;
}

.tree-list {
  flex: 1;
  height: 100%;
  overflow: hidden;
}

.tree-item {
  display: flex;
  justify-content: space-between;
  align-items: center;
  line-height: 1;
  height: 0;
  opacity: 0;
  transition: 0.2s;
  overflow: hidden;
}

.tree-item.show {
  height: 90rpx;
  opacity: 1;
}

.tree-item.showchild:before {
  transform: rotate(90deg);
}

.tree-item.last:before {
  opacity: 0;
}

.switch-on {
  width: 0;
  height: 0;
  border-left: 10rpx solid transparent;
  border-right: 10rpx solid transparent;
  border-top: 15rpx solid #666;
}

.switch-off {
  width: 0;
  height: 0;
  border-bottom: 10rpx solid transparent;
  border-top: 10rpx solid transparent;
  border-left: 15rpx solid #666;
}

.item-last-dot {
  position: absolute;
  width: 10rpx;
  height: 10rpx;
  border-radius: 100%;
  background: #666;
}

.item-icon {
  width: 26rpx;
  height: 26rpx;
  margin-right: 8rpx;
  padding-right: 20rpx;
  padding-left: 20rpx;
}

.item-label {
  flex: 1;
  display: flex;
  align-items: center;
  height: 100%;
  line-height: 1.2;
}

.item-name {
  flex: 1;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  width: 450rpx;
}

.item-check {
  width: 40px;
  height: 40px;
  display: flex;
  justify-content: center;
  align-items: center;
}

.item-check-yes,
.item-check-no {
  width: 20px;
  height: 20px;
  border-top-left-radius: 20%;
  border-top-right-radius: 20%;
  border-bottom-right-radius: 20%;
  border-bottom-left-radius: 20%;
  border-top-width: 1rpx;
  border-left-width: 1rpx;
  border-bottom-width: 1rpx;
  border-right-width: 1rpx;
  border-style: solid;
  border-color: #0055ff;
  display: flex;
  justify-content: center;
  align-items: center;
  box-sizing: border-box;
}

.item-check-yes-part {
  width: 12px;
  height: 12px;
  border-top-left-radius: 20%;
  border-top-right-radius: 20%;
  border-bottom-right-radius: 20%;
  border-bottom-left-radius: 20%;
  background-color: #0055ff;
}

.item-check-yes-all {
  margin-bottom: 5px;
  border: 2px solid #007aff;
  border-left: 0;
  border-top: 0;
  height: 12px;
  width: 6px;
  transform-origin: center;
  /* #ifndef APP-NVUE */
  transition: all 0.3s;
  /* #endif */
  transform: rotate(45deg);
}

.item-check .radio {
  border-top-left-radius: 50%;
  border-top-right-radius: 50%;
  border-bottom-right-radius: 50%;
  border-bottom-left-radius: 50%;
}

.item-check .radio .item-check-yes-b {
  border-top-left-radius: 50%;
  border-top-right-radius: 50%;
  border-bottom-right-radius: 50%;
  border-bottom-left-radius: 50%;
}

.hover-c {
  opacity: 0.6;
}

.itemBorder {
  border-bottom: 1px solid #e5e5e5;
}
</style>
